module kernel.arch.x86.gdt;
import kernel.core.util;
/******************************
 * Port of the old HIMTOS GDT *
 * structure... Probably real *
 * hacky... oh well...        *
 ******************************/

//First we set up or structs for GDT gates
align(1) struct GDTgateOptions {
	uint bfflags;

	//D Style Bitfields... Yuck.
	mixin(Bitfield!(bfflags,"Type",4,"S",1,"DPL",2,"Present",1,"LimitH",4,"AVL",1,"L",1,"DB",1,"Granularity",1));
}

align(1) struct GDTgate {
	ushort LimitL;
	ushort BaseL;
	ubyte BaseHL;
	GDTgateOptions Options;
	ubyte BaseHH;
}

align(1) struct GDTPtr {
	ushort limit;
	uint base;
}	

alias GDTgate LDTgate; //LDT and GDT Gates are the same.. Giggidy :)
const int GDT_SIZE = 256;

/* GDT entry TYPE FIELD, S flag is included */

/* S=1 */
const ubyte GDT_DATA_RO = 0x10; //READ ONLY
const ubyte GDT_DATA_RO_A = 0x11; //READ ONLY, ACCESSED
const ubyte GDT_DATA_RW = 0x12; //READ WRITE
const ubyte GDT_DATA_RW_A = 0x13; //READ WRITE, ACCESSED
const ubyte GDT_DATA_RO_ED = 0x14; //READ ONLY, EXPAND DOWN
const ubyte GDT_DATA_RO_ED_A = 0x15; //READ ONLY, EXPAND DOWN, ACCESSED
const ubyte GDT_DATA_RW_ED = 0x16; //READ WRITE, EXPAND DOWN
const ubyte GDT_DATA_RW_ED_A = 0x17; //READ WRITE, EXPAND DOWN, ACCESSED
const ubyte GDT_CODE_EO = 0x18; //EXECUTE ONLY
const ubyte GDT_CODE_EO_A = 0x19; //EXECUTE ONLY, ACCESSED
const ubyte GDT_CODE_ER = 0x1A; //EXECUTE READ
const ubyte GDT_CODE_ER_A = 0x1B; //EXECUTE READ, ACCESSED
const ubyte GDT_CODE_EO_C = 0x1C; //EXECUTE ONLY, CONFORMING
const ubyte GDT_CODE_EO_C_A = 0x1D; //EXECUTE ONLY CONFORMING, ACCESSED
const ubyte GDT_CODE_ERO_C = 0x1E; //EXECUTE READ ONLY, CONFORMING
const ubyte GDT_CODE_ERO_C_A = 0x1F; //EXECUTE READ ONLY, CONFORMING ACCESSED

/* GDT entry TYPES */
/* S=0 */
const ubyte GDT_LDT = 0x2;
const ubyte GDT_TSS = 0x9;

const ubyte GDT_DPL0 = 0x0;
const ubyte GDT_DPL3 = 0x3;

const ulong MAX_SEG_SIZE = 0xFFFFFFFF;

const ubyte GDT_USED = 1;

const ubyte GDT_G_4KB = 0x1;
const ubyte GDT_G_1B = 0x0;

//#define SEG_SEL(IDX,TI,RPL) ((IDX<<3)+(TI<<2)+RPL) //Don't need this yet...
const ubyte TI_LDT = 1;
const ubyte TI_GDT = 0;

const ushort GLOBAL_LDT = 3;



struct GDT {
	static:
	public:
		bool init() {
			GDTgatesMap[0] = 0;
			initGDTgatesMap();
			initLDTgates();

			/* GDT Descriptors */
			setGDTgate(0,makeNullGDTgate());
			setGDTgate(1,makeGDTgate(MAX_SEG_SIZE, 0x0, GDT_G_4KB, GDT_CODE_ER, GDT_DPL0));
			setGDTgate(2,makeGDTgate(MAX_SEG_SIZE, 0x0, GDT_G_4KB, GDT_DATA_RW, GDT_DPL0));
			setGDTgate(3,makeGDTgate((GDTgate.sizeof*2)-1,cast(uint)&LDTgates, GDT_G_1B, GDT_LDT, GDT_DPL3));

			/* Finally... Setup our register */
			setGDTR(cast(uint)&GDTgates,(GDT_SIZE*GDTgate.sizeof)-1);
			return true;
		}

		int setGDTgate(int i, GDTgate gate) {
			if (i < GDT_SIZE) { //Make sure we /can/ set it...
				GDTgatesMap[i] = GDT_USED;
				GDTgates[i] = gate;
				return i;
			}
			return -1;
		}

		int getFreeGDTgate() {
			//TODO: Lets speed this up.
			for (int i = 0; i < GDT_SIZE; i++) {
				if (GDTgatesMap[i] != GDT_USED) {
					GDTgatesMap[i] = GDT_USED;
					return i;
				}
			}

			return -1;
		}

		void freeGDTgate(int i) {
			if (i < GDT_SIZE) //Make sure we /can/ free it
				GDTgatesMap[i] = 0;
		}

		GDTgate makeGDTgate(ulong limit, ulong base, ubyte G, ubyte type, ubyte DPL) {
			//Don't ask... Seriously.
			GDTgate entry;
			entry.LimitL = limit & 0xFFFF;
			entry.BaseL = base & 0xFFfF;
			entry.BaseHL = (base & 0xFF0000) >> 16;
			entry.Options.Type = type & 0xF;
			entry.Options.S = (type & 0x10) >> 4;
			entry.Options.DPL = DPL;
			entry.Options.Present = 1;
			entry.Options.LimitH = (limit & 0xF0000) >> 16;
			entry.Options.AVL = 1;
			entry.Options.L = 0;
			entry.Options.DB = 1;
			entry.Options.Granularity = G;
			entry.BaseHH = (base & 0xFF000000) >> 24;
			return entry;
		}

		GDTgate makeNullGDTgate() {
			GDTgate entry;
			*(cast(uint *)&entry) = *((cast(uint *)&entry)+1) = 0; //FIXME: May not work as expected
			return entry;
		}
	private:
		GDTgate *GDTgates = cast(GDTgate *)0x3200;
		GDTgate[2] LDTgates;
		ubyte[GDT_SIZE] GDTgatesMap;

		void setGDTR(uint base, ushort limit) {
			volatile asm { /* YAY! History! Our first asm{}! */
				cli; //Clear all interrupts before loading our new GDT!
			}
			gdtr.limit = limit;
			gdtr.base = base;
			volatile asm {
				lgdt gdtr;
			}
		}

		void initGDTgatesMap() {
			for(int i = 0; i < GDT_SIZE; i++)
				GDTgatesMap[i] = 0;
		}

		void initLDTgates() {
			LDTgates[0] = makeGDTgate(MAX_SEG_SIZE, 0x100000, GDT_G_4KB, GDT_CODE_ER, GDT_DPL3);
			LDTgates[1] = makeGDTgate(MAX_SEG_SIZE, 0x100000, GDT_G_4KB, GDT_DATA_RW, GDT_DPL3);
		}
}

private extern(C) GDTPtr gdtr;
